package chapter2.eg3;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Iterator;
import java.util.Set;
import org.junit.Test;

public class EchoServer {
  @Test
  public void testServer() throws IOException {
    ServerSocketChannel server = ServerSocketChannel.open();
    server.configureBlocking(false);
    server.bind(new InetSocketAddress(8080));

    Selector selector = Selector.open();
    server.register(selector, SelectionKey.OP_ACCEPT);
    while (true) {
      int size = selector.select();
      if (size <= 0) {
        continue;
      }

      Set<SelectionKey> keys = selector.selectedKeys();
      Iterator<SelectionKey> iterator = keys.iterator();
      while (iterator.hasNext()) {
        SelectionKey key = iterator.next();
        iterator.remove();

        if (key.isAcceptable()) {
          accept(key, selector);
        } else if (key.isReadable()) {
          read(key, selector);
        } else if (key.isWritable()) {
          write(key);
        }
      }
    }
  }

  private void accept(SelectionKey key, Selector selector) throws IOException {
    ServerSocketChannel server = (ServerSocketChannel) key.channel();
    SocketChannel socketChannel = server.accept();
    socketChannel.configureBlocking(false);
    socketChannel.register(selector, SelectionKey.OP_READ);
  }

  private void read(SelectionKey key, Selector selector) throws IOException {
    SocketChannel channel = (SocketChannel) key.channel();
    ByteBuffer buffer = ByteBuffer.allocate(1024);
    channel.read(buffer);
    handle(buffer);
    channel.register(selector, SelectionKey.OP_WRITE);
  }

  private void handle(ByteBuffer buffer) {
    buffer.flip();
    byte[] bytes = new byte[buffer.remaining()];
    buffer.get(bytes);
    System.out.println("Server receives message: ");
    System.out.println(new String(bytes, StandardCharsets.UTF_8));
  }

  private void write(SelectionKey key) throws IOException {
    SocketChannel channel = (SocketChannel) key.channel();
    response(channel);
  }

  private void response(SocketChannel channel) throws IOException {
    ByteBuffer buffer = ByteBuffer.wrap(("Hello, I'm server. It's: " + new Date()).getBytes());
    channel.write(buffer);
    channel.close();
  }

}
